#include "STableColumnSet.h"
#include "STable.h"
#include "STableSetDlg.h"
#include <core/FileSys.h>
#include <QEvent>
#include <QTableView>
#include <QHeaderView>
#include <QtGlobal>
#include <QScrollBar>
#include <QSettings>
#include <QMouseEvent>
#include <QMenu>
#include <QtGlobal>
#include <set>

STableColumnSet::STableColumnSet(SVirtualTable* t, const QString& id, QObject* p)
	: QObject(p), m_table(t), m_idStr(id)
{
	m_table->table()->installEventFilter(this);
}

void STableColumnSet::addColumnInfo(const ColumnInfo& ci)
{
	Q_ASSERT(m_dftColumnInfos.find(ci.index) == m_dftColumnInfos.end());
	m_dftColumnInfos[ci.index] = ci;
}

void STableColumnSet::setTableWidthFixed(bool fixed)
{
	m_tableWidthFixed = fixed;
}

void STableColumnSet::apply(bool load)
{
	m_doApply = true;
	if (m_saveCfg && load)
		loadCfg();
	else
		m_curColumnInfos = m_dftColumnInfos;

	QTableView* table = m_table->table();
	QStandardItemModel* model = qobject_cast<QStandardItemModel*>(table->model());
	if (model)
	{
		model->setColumnCount(m_curColumnInfos.size());
	}
	QStringList headerLabels;
	for (int col = 0; col < m_dftColumnInfos.size(); ++col)
	{
		table->horizontalHeader()->setSectionHidden(col, m_curColumnInfos[m_dftColumnInfos[col].curIndex].hide);
		headerLabels.push_back(m_dftColumnInfos[col].name);
		if (model)
			model->setHeaderData(col, Qt::Horizontal, (int)m_dftColumnInfos[col].align, Qt::TextAlignmentRole);
	}

	if (model)
		model->setHorizontalHeaderLabels(headerLabels);

	for (auto col : m_curColumnInfos)
	{
		int from = m_table->table()->horizontalHeader()->visualIndex(col.second.index);
		int to = col.second.curIndex;
		if (from == to)
			continue;
		m_table->table()->horizontalHeader()->moveSection(from, to);
	}

	if (m_tableWidthFixed)
	{
		table->horizontalScrollBar()->hide();
		table->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed);
	}
	else
	{
		table->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive);
		table->horizontalHeader()->setStretchLastSection(true);
		for (int col = 0; col < m_dftColumnInfos.size(); ++col)
		{
			if (m_dftColumnInfos[col].widthType == kFixed)
				table->horizontalHeader()->setSectionResizeMode(col, QHeaderView::Fixed);
		}
	}
	m_doApply = false;
}

void STableColumnSet::resizeColumn(bool useDft)
{
	m_doResize = true;
	int viewWidth = m_table->frameGeometry().width() - (m_table->isVScrollBarShown() ? 8 : 0);//默认有垂直滚动条TODO没有

	int totalWidth = 0;
	float totalRatioBase = 0;
	std::vector<int> cols;
	for (int col = 0; col < m_curColumnInfos.size(); ++col)
	{
		const ColumnInfo& ci = m_curColumnInfos[col];
		if(ci.hide)
			continue;

		if (ci.widthType == kFixed || ci.widthType == kManual)//固定宽度或者用户手动调整的，按照默认或用户调整的尺寸
		{
			m_table->table()->horizontalHeader()->resizeSection(col, ci.curWidth);
			viewWidth -= ci.curWidth;
		}
		else
		{
			float ratio = qMax(0.f, ci.ratio);
			totalRatioBase += ci.ratio;
			cols.push_back(col);
		}
	}

	if (qFuzzyCompare(totalRatioBase, 0.f))
	{
		Q_ASSERT("配置错误！");
		totalRatioBase = 0.1;
	}
	
	if (viewWidth < 0)
		viewWidth = 0;

	totalWidth = 0;
	if (!m_tableWidthFixed)
	{
		for (int i = 0; i < cols.size(); ++i)
		{
			//查看默认宽度
			ColumnInfo& ci = m_curColumnInfos[cols[i]];
			int width = qMax(ci.dftWidth, ci.minWidth);
			width = qMax(width, 8);
			
			//如果按比例的宽度更大，使用比例设置
			float ratio = qMax(0.f, ci.ratio);
			int ratioWidth = ci.ratio * viewWidth / totalRatioBase;

			width = qMax(width, ratioWidth);
			if (i == cols.size() - 1 && width + totalWidth < viewWidth)
				width = viewWidth - totalWidth;

			m_table->table()->horizontalHeader()->resizeSection(cols[i], width);
			ci.curWidth = width;
			totalWidth += width;
		}

	}
	else
	{
		for (int i = 0; i < cols.size(); ++i)
		{
			ColumnInfo& ci = m_curColumnInfos[cols[i]];
			int width = ci.ratio * viewWidth / totalRatioBase;
			width = qMax(width, 8);
			if (ci.minWidth > 0 && width < ci.minWidth)
				width = ci.minWidth;

			if (i == cols.size() - 1 && width + totalWidth < viewWidth)
				width = viewWidth - totalWidth;
			m_table->table()->horizontalHeader()->resizeSection(cols[i], width);
			ci.curWidth = width;
			totalWidth += width;
		}
	}

	saveCfg();
	m_doResize = false;
}

void STableColumnSet::applyChange(std::map<int, bool> changes)
{
	bool changed = false;
	for (auto change : changes)
	{
		ColumnInfo& ci = m_curColumnInfos[change.first];
		if(change.second != ci.hide)
			continue;
		ci.hide = !change.second;
		m_table->table()->horizontalHeader()->setSectionHidden(ci.index, ci.hide);
		changed = true;
	}

	if (changed)
		resizeColumn();
}

bool STableColumnSet::isColVisible(int index)
{
	if (m_dftColumnInfos.find(index) != m_dftColumnInfos.end())
	{
		int curIndex = m_dftColumnInfos[index].curIndex;
		if(m_curColumnInfos.find(curIndex) != m_curColumnInfos.end())
			return !m_curColumnInfos[curIndex].hide;
	}
	
	return false;
}

ColumnInfo STableColumnSet::colInfo(int index)
{
	ColumnInfo ci;
	if (m_dftColumnInfos.find(index) != m_dftColumnInfos.end())
		ci = m_dftColumnInfos[index];

	return ci;
}

void STableColumnSet::setSaveCfg(bool v)
{
	m_saveCfg = v;
	if (m_saveCfg)
	{
		connect(m_table->table()->horizontalHeader(), &QHeaderView::sectionMoved, this, &STableColumnSet::onHeaderSectionMoved);
		connect(m_table->table()->horizontalHeader(), &QHeaderView::sectionResized, this, &STableColumnSet::onHeaderSectionResized);
		m_table->table()->horizontalHeader()->viewport()->installEventFilter(this);
	}
}

std::map<int, std::vector<ColumnInfo> > STableColumnSet::groups()
{
	std::map<int, std::vector<ColumnInfo> > group;
	for (const auto& col : m_curColumnInfos)
	{
		group[col.second.group].push_back(col.second);
	}

	for (auto& g : group)
	{
		std::sort(g.second.begin(), g.second.end(), [](const ColumnInfo& c1, const ColumnInfo& c2) {
			return c1.index < c2.index;
			});
	}
	return group;
}

bool STableColumnSet::eventFilter(QObject* watched, QEvent* event)
{
	if (event->type() == QEvent::Resize && watched == m_table->table())
	{
		resizeColumn();
	}
	else if (event->type() == QEvent::MouseButtonPress && watched == m_table->table()->horizontalHeader()->viewport())
	{
		QMouseEvent* me = dynamic_cast<QMouseEvent*>(event);
		if (me->button() == Qt::RightButton)
		{
			QMenu* menu = new QMenu();
			menu->setAttribute(Qt::WA_DeleteOnClose);

			menu->addAction(tr("表格设置"), [=]()
				{
					STableSetDlg dlg(m_table, this);
					dlg.exec();
				});
			menu->addAction(tr("恢复默认"), [=]() {
				restoreDft();
				});
			menu->popup(me->globalPos());
		}
	}
	else if (event->type() == QEvent::MouseMove && watched == m_table->table()->horizontalHeader())
	{
		QMouseEvent* me = dynamic_cast<QMouseEvent*>(event);
		if (me->button() == Qt::LeftButton)
			m_dragHeader = true;
	}
	return QObject::eventFilter(watched, event);
}

void STableColumnSet::saveCfg()
{
	QSettings settings(FileSys::instance()->appCfgDir() + "/TableSettings.ini", QSettings::IniFormat);
	settings.beginGroup(m_idStr);
	for (auto col : m_curColumnInfos)
	{
		settings.setValue(col.second.name + "_index", col.second.curIndex);
		settings.setValue(col.second.name + "_hide", col.second.hide);
		settings.setValue(col.second.name + "_width", col.second.curWidth);
		settings.setValue(col.second.name + "_width_type", col.second.widthType);
	}
	settings.endGroup();
}

void STableColumnSet::loadCfg()
{
	QSettings settings(FileSys::instance()->appCfgDir() + "/TableSettings.ini", QSettings::IniFormat);
	settings.beginGroup(m_idStr);

	std::vector<ColumnInfo> badCols;
	std::map<int, ColumnInfo>	curColumnInfos;
	for (auto col : m_dftColumnInfos)
	{
		if (col.second.widthType == kFixed)
		{
			curColumnInfos[col.second.index] = col.second;
			continue;
		}

		int index = settings.value(col.second.name + "_index", col.second.index).toInt();
		bool hide = settings.value(col.second.name + "_hide", col.second.hide).toBool();
		float width = settings.value(col.second.name + "_width", col.second.dftWidth).toFloat();
		ColWidthType wt = (ColWidthType)settings.value(col.second.name + "_width_type", kAuto).toInt();

		ColumnInfo ci = col.second;
		ci.curIndex = index;
		ci.hide = hide;
		ci.widthType = wt;
		if (width < ci.minWidth)
			width = ci.minWidth;
		ci.curWidth = width;
		auto iter = curColumnInfos.find(index);
		if (iter != curColumnInfos.end())//索引错误！
		{
			badCols.push_back(ci);
		}
		else
		{
			curColumnInfos[ci.curIndex] = ci;
		}
	}
	if (!badCols.empty())
	{
		int maxIndex = curColumnInfos.rbegin()->second.curIndex;
		for (auto& col : badCols)
		{
			col.curIndex = ++maxIndex;
			curColumnInfos[col.curIndex] = col;
		}
	}

	int index = 0;
	for (auto iter = curColumnInfos.begin(); iter != curColumnInfos.end(); ++iter)
	{
		m_curColumnInfos[index] = iter->second;
		m_dftColumnInfos[iter->second.index].curIndex = index;
		++index;
	}
	settings.endGroup();
}

void STableColumnSet::restoreDft()
{
	for (auto& col : m_dftColumnInfos)
		col.second.curIndex = col.second.index;

	apply(false);
	resizeColumn(true);
}

void STableColumnSet::onHeaderSectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex)
{
	if (m_doApply)
		return;

	//保险起见，全来一遍
	std::map<int, ColumnInfo> curColumnInfos;;
	std::vector<ColumnInfo> badCols;
	std::set<int> colsIndex;
	for (int i = 0; i < m_dftColumnInfos.size(); ++i)
	{
		int oldIndex = m_dftColumnInfos[i].curIndex;
		int newIndex = m_table->table()->horizontalHeader()->visualIndex(i);
		bool exist = colsIndex.insert(newIndex).second == false;
		if (!exist)
		{
			curColumnInfos[newIndex] = m_curColumnInfos[oldIndex];
		}
		else
			badCols.push_back(m_curColumnInfos[oldIndex]);
	}

	m_curColumnInfos.clear();
	int index = 0;
	for (auto col : curColumnInfos)
	{
		col.second.curIndex = index;
		m_curColumnInfos[index] = col.second;
		++index;
	}

	for (auto col : badCols)
	{
		col.index = index;
		m_curColumnInfos[index] = col;
		++index;
	}

	for (auto col : m_curColumnInfos)
		m_dftColumnInfos[col.second.index].curIndex = col.second.curIndex;

	saveCfg();
}

void STableColumnSet::onHeaderSectionResized(int logicalIndex, int oldSize, int newSize)
{
	if (!m_dragHeader || m_doApply || m_doResize || m_curColumnInfos[m_dftColumnInfos[logicalIndex].curIndex].widthType == kFixed)
		return;
	m_curColumnInfos[m_dftColumnInfos[logicalIndex].curIndex].curWidth = newSize;
	m_curColumnInfos[m_dftColumnInfos[logicalIndex].curIndex].widthType = kManual;
	saveCfg();
	m_dragHeader = false;
}
